home *** CD-ROM | disk | FTP | other *** search
/ Software USA 4 #12 / Software USA Volume 4.12.iso / mac / LifeStyles / OpenWithCMPlugin OS8 / Source / OpenwithCMPlugin.cp < prev    next >
Encoding:
Text File  |  1998-10-26  |  40.0 KB  |  1,167 lines  |  [TEXT/CWIE]

  1. // OpenWith CMPlugin
  2. //
  3. // Copyright Peter O'Gorman 1998
  4. //
  5. // Parts of this file were taken directly from or are descended from other sources.
  6. // This file is based on Arno's Sample Plugin and ContextTypeCMPlugin (Sample) 
  7. // from the CMM 1.02 SDK.
  8. // Some parts of this file are modified from MoreFiles by Jim Luther.
  9. // GetFinderProcess was taken unchanged from the MacHack "Finder Flocks" by Christopher Evans
  10. // The source file was ScriptableFinder.c by Leonard Rosenthol.
  11.  
  12. // Thank you everyone for the free sources, and examples.
  13.  
  14. // Class Header
  15. #include "OpenwithCMPlugin.h"
  16.  
  17. // Mac OS Includes
  18. #include <CodeFragments.h>
  19. #include <ContextualMenuPlugins.h>
  20. #include <Folders.h>
  21. #include <Aliases.h>
  22. #include <Resources.h>
  23. #include <FinderRegistry.h>
  24. #include <TextUtils.h>
  25. #include <StringCompare.h>
  26.  
  27. // SOM Includes
  28. #include <som.xh>
  29.  
  30.  
  31.  
  32.  
  33. struct BeenThere { /* To check if we have looked at a directory before */
  34.     SInt16 vRefNum;
  35.     SInt32  dirID;
  36. };    
  37. struct IterateGlobals        /* If you are going to include Morefiles then you need to change the name of this */
  38. {
  39.     UInt16                    numItems;
  40.     FSSpec**                applicationsHdl;
  41.     BeenThere**                ER;
  42.     FSSpec                    tempSpec;
  43.     AERecord                theSuperCommand;
  44.     Boolean                    wasAliased;
  45.     Boolean                    targetIsFolder;
  46.     FolderType                theType;
  47.     SInt32                    tempDirID;
  48.     CInfoPBRec                cPB;            /* the parameter block used for PBGetCatInfo calls */
  49.     Str63                    itemName;        /* the name of the current item */
  50.     OSErr                    result;            /* temporary holder of results - saves 2 bytes of stack each level */
  51.     UInt16                    maxLevels;        /* Maximum levels to iterate through */
  52.     UInt16                    currentLevel;    /* The current level IterateLevel is on */            
  53. };
  54. //defines
  55. #define dErr theGlobals->result // saves me typing
  56. #define kHandleInc         10        // the increment for our FSSpec Handle 10 * sizeof(FSSpec)
  57. #define kStringList        982        // STR# resource ID;
  58. #define iPrefFileName     1    
  59. #define iMakeFavourites 2
  60. #define iOpenWith         3
  61.  
  62. // Function declarations
  63. extern pascal OSErr __initialize(CFragInitBlockPtr); // metrowerks's default initializer
  64. extern pascal void __terminate(void);
  65. pascal OSErr OpenwithCMPluginInitialize(CFragInitBlockPtr init); // our initializer
  66. pascal void OpenwithCMPluginTerminate(void); // our terminator
  67.  
  68. OSStatus AddCommandToAEDescList(ConstStr255Param inCommandString,
  69.     SInt32 inCommandID, AEDescList* ioCommandList);
  70.     
  71. OSType GetFileListStatus(    AEDescList* fileList, 
  72.                             Boolean& haveFolders);
  73.  
  74. void ProcessSelection(    AEDescList* fileList,SInt32 CommandID,
  75.                         FSSpec** applications,ProcessSerialNumber finderProcess);
  76.                         
  77. pascal OSErr DoFavourites(FSSpec * inFolder);
  78. pascal OSErr OpenPrefFile(Boolean inCreate,FSSpec * outSpec);
  79. void IterateFavourites(AEDescList* ioCommands,
  80.                         AEDesc * inContextDescriptor, 
  81.                         FSSpec *** theHandle);
  82.  
  83.  
  84. Boolean BeenHereBefore(IterateGlobals *theGlobals,SInt16 vRefNum,SInt32 dirID);
  85.  
  86. static void    MyIterateDirectoryLevel(SInt16 vRefNum,SInt32 dirID,
  87.                                       IterateGlobals *theGlobals,AEDescList * ioCommands);
  88.                                                                    
  89. OSErr    GetFinderProcess (ProcessSerialNumber *finderpsn, Boolean shortcut); 
  90.  
  91. OSStatus HaveThisAPPL(FSSpec theSpec,IterateGlobals *theGlobals,AEDescList * ioCommands); 
  92. inline OSStatus GetIthSpec(AEDescList * inList,FSSpec * outSpec,UInt32 i); 
  93.  
  94. /* these cause linkage warnings with Universal headers 3.2, could rename them, I suppose */                                                                                
  95. pascal OSErr IsAliasFile(const FSSpec *fileFSSpec,
  96.                              Boolean *aliasFileFlag,
  97.                              Boolean *folderFlag);
  98. pascal OSErr ResolveAliasFileWithMountFlags    (FSSpec *                theSpec,
  99.                                  Boolean                 resolveAliasChains,
  100.                                  Boolean *                targetIsFolder,
  101.                                  Boolean *                wasAliased,
  102.                                  unsigned long             mountFlags)     ;  
  103. pascal OSErr ResolveAliasWithMountFlags        (const FSSpec *            fromFile,
  104.                                  AliasHandle             alias,
  105.                                  FSSpec *                target,
  106.                                  Boolean *                wasChanged,
  107.                                  unsigned long             mountFlags)    ;                                                         
  108. // Globals -aargh - Should have made the whole thing C++ and used member variables
  109. SInt16 gPrefRefNum = -1;
  110. SInt16 gPlugRefNum = -1;
  111. FSSpec gMySpec;
  112.  
  113. /*******************************************************************************
  114.  
  115.     OpenwithCMPluginInitialize
  116.     
  117.         All plugin SOM object must somehow register themselves so clients
  118.         can instantiate them by name.  The best place that I've found to
  119.         do it is in the fragment initializer.
  120.  
  121. *******************************************************************************/
  122.  
  123. pascal OSErr OpenwithCMPluginInitialize(CFragInitBlockPtr init)
  124. {
  125. #pragma unused (init)
  126.  
  127.     OSErr theError = __initialize(init);
  128.     if (theError == noErr)
  129.     {
  130.         // register our class with SOM
  131.         somNewClass(OpenwithCMPlugin);
  132.     }
  133.     return theError;
  134. } // OpenwithCMPluginInitialize
  135.  
  136.  
  137.  
  138. /*******************************************************************************
  139.  
  140.     OpenwithCMPlugin::Initialize
  141.  
  142. *******************************************************************************/
  143.  
  144. OSStatus  OpenwithCMPlugin::Initialize(
  145.     Environment*,
  146.     FSSpec* inFileSpec)
  147. {
  148.     // Store Pligins FSSpec;
  149.     BlockMoveData((Ptr)inFileSpec,&gMySpec,sizeof(FSSpec));
  150.  
  151.     return noErr;
  152.     
  153. } // OpenwithCMPlugin::Initialize
  154.  
  155.  
  156.  
  157. /*******************************************************************************
  158.  
  159.     OpenwithCMPlugin::ExamineContext
  160.  
  161. *******************************************************************************/
  162.  
  163. OSStatus  OpenwithCMPlugin::ExamineContext(
  164.     Environment*,
  165.     AEDesc *inContextDescriptor,
  166.     SInt32 inTimeOutInTicks,
  167.     AEDescList* ioCommands,
  168.     Boolean* outNeedMoreTime)
  169. {
  170. #pragma unused(inTimeOutInTicks)
  171.     if (inContextDescriptor != NULL)
  172.     {    
  173.         
  174.         OSErr    theErr = GetFinderProcess (&finderProcess, false); 
  175.         if (theErr == noErr){
  176.         // If the finder is running, we can go on.
  177.             gPlugRefNum = ::FSpOpenResFile(&gMySpec,fsCurPerm);
  178.             if (gPlugRefNum != -1){
  179.                 IterateFavourites(ioCommands,inContextDescriptor,&mFileHandle);
  180.             }    
  181.         }    
  182.     
  183.         
  184.     }
  185.  
  186.     *outNeedMoreTime = false;
  187.     return noErr;
  188.     
  189. } // OpenwithCMPlugin::ExamineContext
  190.  
  191.  
  192.  
  193. /*******************************************************************************
  194.  
  195.     OpenwithCMPlugin::HandleSelection
  196.  
  197. *******************************************************************************/
  198.  
  199. OSStatus OpenwithCMPlugin::HandleSelection(
  200.     Environment*,
  201.     AEDesc *inContextDescriptor,
  202.     SInt32 inCommandID)
  203. {
  204.  
  205.     ProcessSelection(inContextDescriptor,inCommandID,mFileHandle,finderProcess);
  206.  
  207.     return noErr;
  208.     
  209. } // OpenwithCMPlugin::HandleSelection
  210.  
  211.  
  212.  
  213. /*******************************************************************************
  214.  
  215.     OpenwithCMPlugin::PostMenuCleanup
  216.  
  217. *******************************************************************************/
  218.  
  219. OSStatus OpenwithCMPlugin::PostMenuCleanup(
  220.     Environment*)
  221. {
  222.     // Dispose of things 
  223.     ::DisposeHandle((Handle)mFileHandle);
  224.     if (gPrefRefNum != -1){
  225.         CloseResFile(gPrefRefNum);
  226.         gPrefRefNum = -1;
  227.     }
  228.     // Could/should keep the plugin res file open all the time?    
  229.     if (gPlugRefNum != -1){
  230.         CloseResFile(gPlugRefNum);
  231.         gPlugRefNum = -1;
  232.     }
  233.     return noErr;
  234.     
  235. } // OpenwithCMPlugin::PostMenuCleanup
  236.  
  237.  
  238.  
  239. /*******************************************************************************
  240.  
  241.     AddCommandToAEDescList from Apple sample code
  242.  
  243. *******************************************************************************/
  244.  
  245. OSStatus AddCommandToAEDescList(
  246.     ConstStr255Param inCommandString,
  247.     SInt32 inCommandID,
  248.     AEDescList* ioCommandList)
  249. {
  250.     OSStatus theError = noErr;
  251.     
  252.     AERecord theCommandRecord = { typeNull, NULL };
  253.     
  254.     do
  255.     {
  256.         // create an apple event record for our command
  257.         theError = ::AECreateList(NULL, 0, true, &theCommandRecord);
  258.         if (theError != noErr) break;
  259.         
  260.         // stick the command text into the aerecord
  261.         theError = ::AEPutKeyPtr(&theCommandRecord, keyAEName, typeChar,
  262.             &inCommandString[1], inCommandString[0]);
  263.         if (theError != noErr) break;
  264.             
  265.         // stick the command ID into the AERecord
  266.         theError = ::AEPutKeyPtr(&theCommandRecord, keyContextualMenuCommandID, typeLongInteger,
  267.             &inCommandID, sizeof (inCommandID));
  268.         if (theError != noErr) break;
  269.         
  270.         // stick this record into the list of commands that we are passing back to CMM
  271.         theError = ::AEPutDesc(ioCommandList, // the list we're putting our command into
  272.                         0, // stick this command onto the end of our list
  273.                         &theCommandRecord); // the command I'm putting into the list
  274.         
  275.     } while (false);
  276.     // clean up after ourself; dispose of the AERecord
  277.     AEDisposeDesc(&theCommandRecord);
  278.  
  279.     return theError;
  280.     
  281. } // AddCommandToAEDescList
  282.  
  283. // -----------------------------------------------------------------------------------
  284. //    OSStatus GetIthSpec(AEDescList * inList,FSSpec * outSpec,SInt32 i)
  285. // I was doing this in a few places, so made a function of it.
  286. // Input Apple Descriptor List
  287. // Input i - the number
  288. // Output the Spec (must be allocated)
  289. // -----------------------------------------------------------------------------------
  290. inline OSStatus GetIthSpec(AEDescList * inList,FSSpec * outSpec,UInt32 i)
  291. {
  292.  
  293.     OSErr    err = noErr;
  294.     AEDesc file = {typeNull, NULL};
  295.     AEKeyword theKeyword;
  296.     AEDesc coercedFile = {typeNull, NULL};
  297.     if (outSpec == NULL) return paramErr; // sanity        
  298.     do {
  299.         err = ::AEGetNthDesc(inList, i, typeWildCard, &theKeyword, &file);
  300.         if (err != noErr) break;
  301.         // Try to get an FSSpec out of the item in the descriptor; make sure to
  302.         // coerce it, cuz the app may have passed an object specifier.
  303.         AEDesc coercedFile = {typeNull, NULL};
  304.         err = ::AECoerceDesc(&file, typeFSS, &coercedFile);
  305.         if (err != noErr) break;
  306.         if (coercedFile.descriptorType == typeNull){
  307.             err = fnfErr;
  308.             break;
  309.         } 
  310.         // Pull the FSSpec out of the descriptor
  311.     ::BlockMoveData(*coercedFile.dataHandle, outSpec,
  312.         ::GetHandleSize(coercedFile.dataHandle));    
  313.     }while(false);    
  314.  
  315.     // Disposed of the AEDesc
  316.     AEDisposeDesc(&file);
  317.     AEDisposeDesc(&coercedFile);
  318.     return err;
  319. }
  320.  
  321. // ---------------------------------------------------------------------------
  322. // GetFileListStatus // modified from Arno's Sample Plugin that came with CMM SDK
  323. // ---------------------------------------------------------------------------
  324. // Return if all the files are expandable or compressable. 
  325. // Return the number of files in the selection
  326.  
  327. OSType 
  328. GetFileListStatus(    AEDescList* fileList, 
  329.                     Boolean& haveFolders)
  330. {
  331.     OSErr err;
  332.     OSType firstCreator = 0;
  333.     haveFolders = false;
  334.     Boolean itisMe = false;
  335.     Boolean HaveValidFiles = false;
  336.     Boolean HaveUnresolvedAliases = false;
  337.     Boolean targetIsFolder,wasAliased;
  338.     SInt32 listItemsCount = 0;
  339.     FSSpec fileSpec;
  340.     if ( AECountItems(fileList, &listItemsCount)== noErr)
  341.     {    
  342.         for (UInt32 i = 1; i <= listItemsCount; i++)
  343.         {        
  344.             err = GetIthSpec(fileList,&fileSpec,i);
  345.             if (err != noErr) break;
  346.             // Get info about the file
  347.             CInfoPBRec fileInfo;
  348.             fileInfo.hFileInfo.ioCompletion = NULL; // synchronous
  349.             fileInfo.hFileInfo.ioNamePtr = fileSpec.name;
  350.             fileInfo.hFileInfo.ioVRefNum = fileSpec.vRefNum;
  351.             fileInfo.hFileInfo.ioFDirIndex = 0; // search for the named item
  352.             fileInfo.hFileInfo.ioDirID = fileSpec.parID;
  353.             err = ::PBGetCatInfoSync(&fileInfo);
  354.             if (err != noErr) break;
  355.             /* Is it An Alias */
  356.             if ( (fileInfo.hFileInfo.ioFlAttrib & ioDirMask) == 0 ){
  357.                 if ( (fileInfo.hFileInfo.ioFlFndrInfo.fdFlags & 0x8000) != 0){    
  358.                     err = ResolveAliasFileWithMountFlags(&fileSpec,
  359.                                                           true,
  360.                                                          &targetIsFolder,
  361.                                                              &wasAliased,
  362.                                                             kResolveAliasFileNoUI);
  363.                     if (err != noErr) { 
  364.                         HaveUnresolvedAliases = true;
  365.                         err = noErr;    
  366.                         continue;            
  367.                     }    
  368.                     fileInfo.dirInfo.ioFDirIndex = 0;
  369.                     fileInfo.hFileInfo.ioVRefNum = fileSpec.vRefNum;
  370.                     fileInfo.dirInfo.ioDrDirID = fileSpec.parID;
  371.                     fileInfo.hFileInfo.ioNamePtr = (StringPtr)&fileSpec.name;
  372.                     err = PBGetCatInfoSync((CInfoPBPtr)&fileInfo);
  373.                     if (err != noErr){
  374.                         break;
  375.                     }
  376.                  }
  377.              }
  378.             // see if we have a folder
  379.             if ((fileInfo.hFileInfo.ioFlAttrib & ioDirMask) != 0)
  380.             {
  381.                 FolderType theType = nil;
  382.                 err = IdentifyFolder(    fileInfo.hFileInfo.ioVRefNum,
  383.                                         fileInfo.hFileInfo.ioDirID,
  384.                                         &theType);
  385.                 if (err != noErr){
  386.                     haveFolders = true;
  387.                     err = noErr;
  388.                 }                        
  389.                 else if ((theType != kTrashFolderType) &&
  390.                         (theType !=     kFontsFolderType) &&
  391.                         (theType !=     kExtensionFolderType)) // we could go wild here...                        
  392.                     haveFolders = true;
  393.                 else {
  394.                     firstCreator = 'Err ';
  395.                 }        
  396.             }
  397.             else if ((fileInfo.hFileInfo.ioFlAttrib & ioDirMask) == 0)
  398.             {
  399.                 if ((fileInfo.hFileInfo.ioFlFndrInfo.fdType == 'cmnu') &&
  400.                     (fileSpec.vRefNum == gMySpec.vRefNum) &&
  401.                     (fileSpec.parID == gMySpec.parID) &&
  402.                     (EqualString(fileSpec.name,gMySpec.name,false,false)))
  403.                         itisMe = true;
  404.                 else {
  405.                     HaveValidFiles = true;
  406.                 }        
  407.             }
  408.  
  409.         }
  410.     }
  411.     if (haveFolders && (listItemsCount == 1)) {
  412.             firstCreator = 'fold';
  413.     }else
  414.     if ((!haveFolders) && (itisMe) && (listItemsCount == 1)) firstCreator = ' ME ';
  415.     else
  416.     if ((!haveFolders) && (!itisMe) && (HaveUnresolvedAliases) && (!HaveValidFiles)) firstCreator = 'Err ';
  417.     if (err != noErr) firstCreator = 'Err ';
  418. return firstCreator;
  419. }
  420. // ---------------------------------------------------------------------------
  421. // void ProcessSelection(AEDescList* fileList,
  422. //                        SInt32 CommandID,
  423. //                        FSSpec** applications,
  424. //                        ProcessSerialNumber finderProcess)
  425. // ---------------------------------------------------------------------------
  426. // Handle User Choice;
  427. // Inputs    - AEDescList* fileList     - the file selection
  428. //            - SInt32 CommandID       - either -32000 MAke Favourites folder of the FSSpec index
  429. //            - FSSpec** applications    - Our FSSpec handle
  430. //            - the finder PSN
  431. // Returns void
  432.  
  433. void ProcessSelection(AEDescList* fileList,SInt32 CommandID,FSSpec** applications,ProcessSerialNumber finderProcess)
  434. {
  435.     OSErr err;
  436.     OSType firstCreator = nil;
  437.     SInt32 listItemsCount = 0;
  438.     AEDesc file = {typeNull, NULL};
  439.     FSSpec fileSpec;
  440.     
  441.     if (CommandID == -32000){ // Set Favourite folder
  442.         AliasHandle alisHandle;
  443.         Boolean wasChanged;
  444.         OSErr theErr;
  445.         Boolean wasAliased, targetIsFolder;
  446.         theErr = OpenPrefFile(true,NULL);
  447.         UseResFile(gPrefRefNum);
  448.         alisHandle = (AliasHandle)::Get1IndResource(rAliasType, 1);
  449.         theErr = ResError();
  450.         theErr = GetIthSpec(fileList,&fileSpec,1);
  451.         if (theErr == noErr) {
  452.             err = ResolveAliasFileWithMountFlags(&fileSpec,
  453.                                       true,
  454.                                      &targetIsFolder,
  455.                                          &wasAliased,
  456.                                         kResolveAliasFileNoUI);
  457.             if (alisHandle == nil){
  458.                 theErr = ::NewAlias(nil,&fileSpec,&alisHandle);
  459.                 if (theErr == noErr){
  460.                     AddResource((Handle)alisHandle, rAliasType, 0, fileSpec.name);
  461.                     WriteResource((Handle)alisHandle);
  462.                     theErr = ResError();
  463.                 }
  464.             }
  465.             else {
  466.                 theErr = UpdateAlias(nil,&fileSpec,alisHandle,&wasChanged);
  467.             }
  468.             ChangedResource((Handle)alisHandle);
  469.             WriteResource((Handle)alisHandle);
  470.         }
  471.     return;
  472.     }    // End Set favourite folder - should have been a function
  473.     
  474.     if (AECountItems(fileList, &listItemsCount) == noErr)
  475.     {    
  476.         AppleEvent theOpenEvent,theReply;
  477.         AEDesc theFinder = {typeNull,NULL};
  478.         AEDesc theApplication = {typeNull,NULL};
  479.         AEDescList theDocuments = {typeNull,NULL};
  480.         AliasHandle aDocument;
  481.         err = ::AECreateList(nil,0,false,&theDocuments);
  482.         if (err != noErr) return;
  483.         
  484.         for (UInt32 i = 1; i <= listItemsCount; i++)
  485.         {
  486.  
  487.             err = GetIthSpec(fileList, &fileSpec,i);
  488.             if (err != noErr) break;
  489.             err = NewAliasMinimal(&fileSpec, &aDocument); 
  490.             if (err == noErr){
  491.                 HLock((Handle)aDocument);
  492.                 err = AEPutPtr(&theDocuments, 0, typeAlias, *aDocument, GetHandleSize((Handle)aDocument)); 
  493.                 DisposeHandle((Handle)aDocument);
  494.             }    
  495.         }        
  496.  
  497.         // Now We have the documents in a list we know is acceptable 
  498.         // probably didn't need the above, as we could have used the list we were passed
  499.         // but..
  500.         do {
  501.             if (CommandID < 0) break; // am I sane?
  502.             err = AECreateDesc(typeProcessSerialNumber, &finderProcess, 
  503.                                 sizeof(finderProcess), &theFinder);
  504.             if (err != noErr) break;                    
  505.             err = AECreateAppleEvent(kCoreEventClass, kAEOpenDocuments, 
  506.                                     &theFinder, kAutoGenerateReturnID, 
  507.                                     kAnyTransactionID, &theOpenEvent);
  508.             if (err != noErr) break;
  509.             err = AEPutParamDesc(&theOpenEvent, keyDirectObject, &theDocuments); 
  510.             if (err != noErr) break;
  511.             HLock((Handle)applications);    
  512.             err = NewAlias(nil,&(*applications)[CommandID],&aDocument);
  513.             HUnlock((Handle)applications);
  514.             if (err != noErr) break;
  515.             HLock((Handle)aDocument);
  516.             err = AECreateDesc(typeAlias, *aDocument, 
  517.                                 ::GetHandleSize((Handle)aDocument), &theApplication);
  518.             DisposeHandle((Handle)aDocument);                    
  519.             if (err != noErr) break;
  520.             err = AEPutParamDesc(&theOpenEvent, keyAEUsing, &theApplication);
  521.             
  522.             err = AESend(&theOpenEvent, &theReply, kAENoReply, kAENormalPriority, 
  523.                                 kAEDefaultTimeout, nil, nil);
  524.                                 
  525.             } while (false);
  526.             
  527.         AEDisposeDesc(&theFinder);
  528.         AEDisposeDesc(&theApplication);
  529.         AEDisposeDesc(&theDocuments);
  530.         AEDisposeDesc(&theOpenEvent);
  531.         AEDisposeDesc(&theReply);                                                            
  532.         
  533.     }
  534.  
  535. return;
  536. }
  537.  
  538. // -----------------------------------------------------------
  539. // pascal OSErr OpenPrefFile(Boolean inCreate, FSSpec * PrefSpec)
  540. // -----------------------------------------------------------
  541. // Opens our preferences file
  542. // Input    inCreate - if true creates a preffile if there wasn't one.
  543. // Output    PrefSpec - Our preffiles FSSpec.
  544. pascal OSErr OpenPrefFile(Boolean inCreate, FSSpec * PrefSpec)
  545. {
  546.     OSErr theErr = noErr;
  547.     SInt16 vRefNum;
  548.     SInt32 dirID;
  549.     Str255 myFileName;
  550.     FSSpec myPrefFile;
  551.     if (gPrefRefNum != -1) return noErr;
  552.     do {
  553.  
  554.         GetIndString(myFileName,kStringList,iPrefFileName);            
  555.         theErr = FindFolder(kOnSystemDisk,kPreferencesFolderType,kDontCreateFolder,&vRefNum,&dirID);
  556.         if (theErr != noErr) break;
  557.         theErr = FSMakeFSSpec(vRefNum,dirID,myFileName,&myPrefFile);
  558.         if (PrefSpec != NULL) BlockMoveData((Ptr)&myPrefFile,PrefSpec,sizeof(FSSpec));
  559.         if ((theErr != noErr) && (theErr != fnfErr)) break;
  560.         if ((inCreate) && (theErr == fnfErr)){
  561.             FSpCreateResFile(&myPrefFile, '????', 'pref', smSystemScript);
  562.             theErr = ResError();
  563.         }
  564.         if (theErr != noErr) break;
  565.         gPrefRefNum = FSpOpenResFile(&myPrefFile, fsRdWrPerm);
  566.         theErr = ResError();
  567.     } while (false);
  568.     return theErr;
  569. }    
  570.  
  571.  
  572. // -----------------------------------------------------------
  573. //    pascal OSErr DoFavourites(FSSpec * inFolder)
  574. // -----------------------------------------------------------    
  575. // REsolves the alias for the favourites folder returns the resolved FSSpec        
  576. pascal OSErr DoFavourites(FSSpec * inFolder)
  577. {
  578.  
  579.     OSErr theErr;
  580.     Boolean targetIsFolder;
  581.        Boolean wasChanged;
  582.     Handle alisHandle = nil;
  583.        do {
  584.         OpenPrefFile(false,inFolder);
  585.         if (gPrefRefNum == -1) break;
  586.         UseResFile(gPrefRefNum);
  587.         /* the first 'alis' resource in the file is the appropriate alias */
  588.         alisHandle = Get1IndResource(rAliasType, 1);
  589.         theErr = ResError();
  590.         if (alisHandle == nil) break;
  591.         /* load the resource explicitly in case SetResLoad(FALSE) */
  592.         LoadResource(alisHandle);
  593.         theErr = ResError();
  594.         if (theErr != noErr) break;
  595.         theErr = ResolveAliasWithMountFlags( nil,
  596.                                              (AliasHandle)alisHandle,
  597.                                              inFolder,
  598.                                              &wasChanged,
  599.                                              kResolveAliasFileNoUI);
  600.         theErr = IsAliasFile(inFolder,
  601.                             &wasChanged,
  602.                             &targetIsFolder);                                             
  603.     } while (false) ;               
  604.        if ((theErr == noErr) && targetIsFolder){
  605.            return noErr;
  606.        }
  607.  
  608.     return fnfErr;
  609. }
  610.  
  611. // -----------------------------------------------------------    
  612. // void IterateFavourites(AEDescList* ioCommands,AEDesc * inContextDescriptor,FSSpec *** theHandle)
  613. // -----------------------------------------------------------    
  614. // Adds the commands
  615. // Returns an FSSpec handle with the found applications
  616. void IterateFavourites(AEDescList* ioCommands,AEDesc * inContextDescriptor,FSSpec *** theHandle)
  617. {
  618. // Should rewrite the first part of this.
  619. IterateGlobals    theGlobals;
  620.     OSErr            result = noErr;
  621.     SInt32            theDirID;
  622.     SInt16            theVRefNum;
  623.     FSSpec            inFolder;
  624.     SInt16            numItems = 0;
  625.         // 
  626.     Boolean haveFolders;
  627.     OSType theCreator = GetFileListStatus(    inContextDescriptor, haveFolders);
  628.     if (theCreator == 'Err '){
  629.         return;
  630.     }
  631.     theGlobals.numItems = 0;    
  632.     theGlobals.applicationsHdl = (FSSpec**)TempNewHandle(sizeof(FSSpec)*kHandleInc,&result);
  633.     if ((theGlobals.applicationsHdl == nil) || (result != noErr)) return;
  634.     theGlobals.result = noErr;            /* temporary holder of results - saves 2 bytes of stack each level */
  635.     AEDescList mySubMenu = {typeNull,NULL};
  636.     result = AECreateList(NULL,0,false,&mySubMenu);
  637.     if (result == noErr){
  638.         if (theCreator == 'fold') {
  639.          // Updated 10/20/98 Now checks if the directory if already the favourite
  640.                 FSSpec folderSpec;
  641.                 FSSpec inFolderSpec;
  642.                 result = DoFavourites(&folderSpec);
  643.                 if (result == noErr){
  644.                 result = GetIthSpec(inContextDescriptor,&inFolderSpec,1);
  645.                 if (result == noErr){
  646.                 if ((folderSpec.vRefNum != inFolderSpec.vRefNum) || 
  647.                     (folderSpec.parID != inFolderSpec.parID) ||
  648.                     !(EqualString(folderSpec.name,inFolderSpec.name,false,false))){
  649.                     Str255 aString;
  650.                     GetIndString(aString,kStringList,iMakeFavourites);
  651.                 AddCommandToAEDescList(aString,-32000,ioCommands);
  652.                 }
  653.                 }
  654.                 }
  655.         }
  656.         else if (!haveFolders){
  657.  
  658.  
  659.             result = DoFavourites(&inFolder);
  660.             if (result == noErr){
  661.                 theGlobals.cPB.dirInfo.ioFDirIndex = 0;
  662.                 theGlobals.cPB.hFileInfo.ioVRefNum = inFolder.vRefNum;
  663.                 theGlobals.cPB.dirInfo.ioDrDirID = inFolder.parID;
  664.                 theGlobals.cPB.hFileInfo.ioNamePtr = (StringPtr)&inFolder.name;
  665.                 result = PBGetCatInfoSync((CInfoPBPtr)&theGlobals.cPB);
  666.                 if ( result == noErr )
  667.                 {
  668.  
  669.                     if ( (theGlobals.cPB.hFileInfo.ioFlAttrib & ioDirMask) != 0 )
  670.                     {
  671.                         theDirID = theGlobals.cPB.dirInfo.ioDrDirID;
  672.                         theVRefNum = theGlobals.cPB.hFileInfo.ioVRefNum;
  673.                         /* Set up the globals we need to access from the recursive routine. */
  674.                         theGlobals.cPB.hFileInfo.ioNamePtr = (StringPtr)&theGlobals.itemName;
  675.                         theGlobals.cPB.hFileInfo.ioVRefNum = theVRefNum;
  676.                         theGlobals.itemName[0] = 0;
  677.                         theGlobals.result = noErr;
  678.                         theGlobals.theSuperCommand.descriptorType = typeNull;
  679.                         theGlobals.theSuperCommand.dataHandle = NULL;
  680.                         theGlobals.maxLevels = 4;          /* seems to be like a 'Magic Number' */
  681.                         theGlobals.currentLevel = 0;    /* start at level 0 */
  682.                         theGlobals.ER = (BeenThere**)TempNewHandle(0,&result);
  683.                         if ((theGlobals.ER != nil) && (result == noErr)){
  684.                         /* Here we go into recursion land... */
  685.                             MyIterateDirectoryLevel(theVRefNum,theDirID, &theGlobals,&mySubMenu);
  686.                             ::DisposeHandle((Handle)theGlobals.ER);
  687.                             result = theGlobals.result;    /* set the result */
  688.                         }    
  689.                     }
  690.                     
  691.                     else
  692.                     {
  693.                         result = dirNFErr;    /* a file was passed instead of a directory */
  694.                     }
  695.                 }
  696.  
  697.             }
  698.         }
  699.         if (AECountItems(&mySubMenu,&theGlobals.tempDirID) == noErr){
  700.  
  701.             if (theGlobals.tempDirID > 0){
  702.                 result = ::AECreateList(NULL,0,true,&theGlobals.theSuperCommand);
  703.                 if (result == noErr){
  704.                 Str255 theSupercommandText;
  705.                     GetIndString(theSupercommandText,kStringList,iOpenWith);
  706.                     result = ::AEPutKeyPtr(&theGlobals.theSuperCommand, keyAEName, typeChar,
  707.                     &theSupercommandText[1], theSupercommandText[0]);
  708.                     if (result == noErr){
  709.                         result = ::AEPutKeyDesc(&theGlobals.theSuperCommand, keyContextualMenuSubmenu,
  710.                                         &mySubMenu);
  711.                         if (result == noErr){
  712.                             result = ::AEPutDesc(ioCommands, // the list we're putting our command into
  713.                                                 0, // stick this command onto the end of our list
  714.                                                 &theGlobals.theSuperCommand); // the command I'm putting into the list            
  715.                         }
  716.                     }
  717.                 AEDisposeDesc(&theGlobals.theSuperCommand);
  718.                 }    
  719.             }
  720.  
  721.         }    
  722.     AEDisposeDesc(&mySubMenu);    
  723.     }
  724.     *theHandle = theGlobals.applicationsHdl;        
  725.     return ;
  726. }
  727.  
  728.                                                     
  729.  
  730.                                                 
  731.  
  732.  
  733.  
  734. //*****************************************************************************
  735. // static void    MyIterateDirectoryLevel(SInt16 vRefNum,SInt32 dirID,
  736. //                                      IterateGlobals *theGlobals,AEDescList * ioCommands)
  737. //
  738. //    
  739. //*****************************************************************************
  740. //
  741. // The main routine, taken from Morefiles IterateDirectory.c and modified to work with aliases
  742. // Most of the bugs were here, hopefully killed most of them.
  743. static void    MyIterateDirectoryLevel(SInt16 vRefNum,SInt32 dirID,
  744.                                       IterateGlobals *theGlobals,AEDescList * ioCommands)
  745. {
  746.  
  747.     
  748.     if ( (theGlobals->maxLevels == 0) ||                        /* if maxLevels is zero, we aren't checking levels */
  749.          (theGlobals->currentLevel < theGlobals->maxLevels) )    /* if currentLevel < maxLevels, look at this level */
  750.         {
  751.         if (!BeenHereBefore(theGlobals,vRefNum,dirID)){
  752.             SInt16 index = 1;
  753.             AEDescList ioSubMenuCommands = {typeNull,NULL}; // adds to stack space but can't avoid it.
  754.             SInt16 saveVref;
  755.             SInt32 saveDirID;
  756.             ++theGlobals->currentLevel;    /* go to next level */
  757.             
  758.             do
  759.             {    /* Isn't C great... What I'd give for a "WITH theGlobals DO" about now... */
  760.             
  761.                 /* Get next source item at the current directory level */
  762.                 theGlobals->itemName[0] = 0;
  763.                 theGlobals->cPB.hFileInfo.ioNamePtr = (StringPtr)&theGlobals->itemName;
  764.                 theGlobals->cPB.dirInfo.ioFDirIndex = index;
  765.                 theGlobals->cPB.hFileInfo.ioVRefNum = vRefNum;
  766.                 theGlobals->cPB.dirInfo.ioDrDirID = dirID;
  767.                     
  768.                 dErr = PBGetCatInfoSync((CInfoPBPtr)&theGlobals->cPB);    
  769.                 if (dErr != noErr) break;
  770.  
  771.                 /* Is it An Alias */
  772.                 if ( (theGlobals->cPB.hFileInfo.ioFlAttrib & ioDirMask) == 0 ){
  773.                     if ( (theGlobals->cPB.hFileInfo.ioFlFndrInfo.fdFlags & 0x8000) != 0){
  774.                         
  775.                         dErr=::FSMakeFSSpec(vRefNum,
  776.                                             dirID,
  777.                                             theGlobals->cPB.hFileInfo.ioNamePtr,
  778.                                             &theGlobals->tempSpec);
  779.                         if (dErr != noErr) break;        // shouldn't happen            
  780.                         dErr = ResolveAliasFileWithMountFlags(&theGlobals->tempSpec,
  781.                                                               true,
  782.                                                              &theGlobals->targetIsFolder,
  783.                                                                  &theGlobals->wasAliased,
  784.                                                                 kResolveAliasFileNoUI);
  785.                         if (dErr != noErr) {     // If the alias can't be resolved, it isn't
  786.                             dErr = noErr;         // a problem.
  787.                             index++;            // <- this is very important
  788.                             continue;
  789.                         }    
  790.                         theGlobals->cPB.dirInfo.ioFDirIndex = 0;
  791.                         theGlobals->cPB.hFileInfo.ioVRefNum = theGlobals->tempSpec.vRefNum;
  792.                         theGlobals->cPB.dirInfo.ioDrDirID = theGlobals->tempSpec.parID;
  793.                         theGlobals->cPB.hFileInfo.ioNamePtr = (StringPtr)&theGlobals->tempSpec.name;
  794.                         dErr = PBGetCatInfoSync((CInfoPBPtr)&theGlobals->cPB);
  795.                         if (dErr != noErr) break; // no Big deal.
  796.  
  797.                      }
  798.                  }
  799.                  dErr=::FSMakeFSSpec(theGlobals->cPB.hFileInfo.ioVRefNum,
  800.                                     theGlobals->cPB.dirInfo.ioDrParID,
  801.                                     theGlobals->cPB.hFileInfo.ioNamePtr,
  802.                                     &theGlobals->tempSpec);
  803.                 if (dErr != noErr) break;                    
  804.                 // Did we find an Application?
  805.                 // I decided not to check appe or APPC, as they usually can't open much                                                  
  806.                 if ( (theGlobals->cPB.hFileInfo.ioFlAttrib & ioDirMask) == 0 ){
  807.                         if (theGlobals->cPB.hFileInfo.ioFlFndrInfo.fdType == 'APPL'){    
  808.                             dErr = HaveThisAPPL(theGlobals->tempSpec,theGlobals,ioCommands);
  809.                             if (dErr != noErr) break; // probably won't happen, but would be memFullErr
  810.                 
  811.                         }
  812.                     }
  813.                  else    { // it is a directory                        
  814.                     dErr = ::AECreateList(NULL,0,false,&ioSubMenuCommands);
  815.                     if (dErr != noErr) break;                
  816.                     saveVref = theGlobals->cPB.hFileInfo.ioVRefNum;
  817.                     saveDirID = theGlobals->cPB.dirInfo.ioDrDirID;    
  818.                     dErr = IdentifyFolder(    saveVref,
  819.                                         saveDirID,
  820.                                         &theGlobals->theType);
  821.                     if (dErr != noErr){
  822.                         dErr = noErr;
  823.                     }
  824.                     else if ((theGlobals->theType == kTrashFolderType ) ||    // skip these types
  825.                             ( theGlobals->theType == kFontsFolderType)){
  826.                             index++;
  827.                             continue;
  828.                     }                                
  829.                     // Recursion here                            
  830.                     MyIterateDirectoryLevel(saveVref,saveDirID, theGlobals,&ioSubMenuCommands);        
  831.                     if (dErr != noErr) break;
  832.                                             
  833.                     dErr = ::AECountItems(&ioSubMenuCommands,&theGlobals->tempDirID); // it is an SInt32.
  834.                     if (dErr != noErr) break;
  835.                     if ( theGlobals->tempDirID > 0 ){ // some items are in the list
  836.                         // Saves stack space by not having to store the name.
  837.                         theGlobals->itemName[0] = 0;
  838.                         theGlobals->cPB.hFileInfo.ioNamePtr = (StringPtr)&theGlobals->itemName;
  839.                         theGlobals->cPB.dirInfo.ioFDirIndex = index;
  840.                         theGlobals->cPB.hFileInfo.ioVRefNum = vRefNum;
  841.                         theGlobals->cPB.dirInfo.ioDrDirID = dirID;
  842.                         dErr = PBGetCatInfoSync((CInfoPBPtr)&theGlobals->cPB);    
  843.                         if (dErr != noErr) break;    
  844.                         // Create the SubMenu Holder, if the number of items in the sub menu is more than zero
  845.                         dErr = ::AECreateList(NULL,0,true,&theGlobals->theSuperCommand);
  846.                         if (dErr != noErr) break;
  847.                         dErr = ::AEPutKeyPtr(&theGlobals->theSuperCommand, 
  848.                                                 keyAEName, typeChar, 
  849.                                                 &theGlobals->itemName[1], 
  850.                                                 (char)theGlobals->itemName[0]);
  851.                         if (dErr != noErr) break;
  852.                         dErr = AEPutKeyDesc(&theGlobals->theSuperCommand, 
  853.                                             keyContextualMenuSubmenu, 
  854.                                             &ioSubMenuCommands);
  855.                         if (dErr != noErr) break;
  856.                         // I know I dispose of things multiple times, but I want to free up memory
  857.                         // as soon as possible.
  858.                         AEDisposeDesc(&ioSubMenuCommands);
  859.                         dErr = AEPutDesc(ioCommands,0,&theGlobals->theSuperCommand);
  860.                         if (dErr != noErr) break;
  861.                         AEDisposeDesc(&theGlobals->theSuperCommand);
  862.                     }    
  863.  
  864.                     AEDisposeDesc(&ioSubMenuCommands);
  865.                 }                 
  866.             ++index; /* prepare to get next item */
  867.             } while ( (dErr == noErr) ); /* time to fall back a level? */
  868.             AEDisposeDesc(&theGlobals->theSuperCommand);
  869.             AEDisposeDesc(&ioSubMenuCommands);
  870.             if ( (dErr == fnfErr) ||    /* fnfErr is OK - it only means we hit the end of this level */
  871.                  (dErr == afpAccessDenied) ) /* afpAccessDenied is OK, too - it only means we cannot see inside a directory */
  872.             {
  873.                 dErr = noErr;
  874.             }            
  875.             --theGlobals->currentLevel;    /* return to previous level as we leave */
  876.         }        
  877.     }
  878. }
  879.  
  880.  
  881. /*
  882. Have we seen this directory before? Due to the inclusion of aliases we could get Apples
  883. Address - 1 Infinite loop. Therefore we must check.
  884. */
  885. Boolean
  886. BeenHereBefore(IterateGlobals *theGlobals,SInt16 vRefNum,SInt32 dirID)
  887. {
  888.     ::HLock((Handle)theGlobals->ER);
  889.     BeenThere * myPtr = *theGlobals->ER;
  890.     Size lByteCnt = ::GetHandleSize((Handle)theGlobals->ER);
  891.     UInt32    numEntries = lByteCnt / sizeof(BeenThere);
  892.     for (UInt32 i = 0;i < numEntries; i++){
  893.         if ((myPtr[i].vRefNum == vRefNum) && (myPtr[i].dirID == dirID)){
  894.             ::HUnlock((Handle)theGlobals->ER);    
  895.              return true;
  896.          }    
  897.     }
  898.     ::HUnlock((Handle)theGlobals->ER);
  899.     ::SetHandleSize((Handle)theGlobals->ER, lByteCnt + sizeof(BeenThere));
  900.     if (MemError() != noErr) return true;
  901.     ::HLock((Handle)theGlobals->ER);
  902.     myPtr = *theGlobals->ER;
  903.     myPtr[numEntries].vRefNum = vRefNum;
  904.     myPtr[numEntries].dirID = dirID;
  905.     HUnlock((Handle)theGlobals->ER);
  906.     return false;
  907. }        
  908.  
  909.  
  910. // -----------------------------------------------------------    
  911. // OSErr    GetFinderProcess (ProcessSerialNumber *finderpsn, Boolean shortcut)
  912. // -----------------------------------------------------------    
  913. // Taken unmodified from ScriptableFinder.c
  914. OSErr    GetFinderProcess (ProcessSerialNumber *finderpsn, Boolean shortcut)
  915.  
  916. /*    A routine to determine the process serial number of the Finder. It returns the result as a
  917.     parameter passed by reference. It also has a Boolean parameter which specifies whether or not
  918.     the returned process serial number will represent the Finder process serial number as
  919.     "kCurrentProcess" if it is the current process to allow a shortcut of the _GetNextEvent call
  920.     dependencies of processing an AppleEvent.
  921.  
  922.      Input:    shortcut - allow "kCurrentProcess" to be used if we are in the current process.
  923.      Input:    *finderpsn - result ProcessSerialNumber passed by reference.
  924.  
  925.     Output:    error code that occured. */
  926.  
  927. {
  928.     Boolean                result;
  929.     ProcessSerialNumber    psn, currentpsn;
  930.     ProcessInfoRec        pir;
  931.     #define kFinderType  'FNDR'
  932.     #define kFinderSignature 'MACS'
  933.     psn.highLongOfPSN = 0;
  934.     psn.lowLongOfPSN = kNoProcess;
  935.     pir.processInfoLength = sizeof(ProcessInfoRec);
  936.     pir.processName = nil;            // don't want these bits of information
  937.     pir.processAppSpec = nil;
  938.     while (GetNextProcess(&psn) == noErr) 
  939.         if (GetProcessInformation(&psn, &pir) == noErr) 
  940.             if ((pir.processType == kFinderType) && (pir.processSignature == kFinderSignature)) 
  941.             {
  942.                 if (shortcut && (GetCurrentProcess(¤tpsn) == noErr) && 
  943.                     (SameProcess(¤tpsn, &psn, &result) == noErr) && result) 
  944.                 {            // use the current process to shortcut the event dispatching
  945.                     finderpsn->highLongOfPSN = 0;
  946.                     finderpsn->lowLongOfPSN = kCurrentProcess;
  947.                 }
  948.                 else
  949.                     *finderpsn = psn;            // found the Finder's psn
  950.                 return(noErr);            // got the process serial number
  951.             }
  952.     return(procNotFound);            // got an error - not found
  953. }
  954.  
  955. // -----------------------------------------------------------
  956. // OSStatus HaveThisAPPL(FSSpec theSpec,IterateGlobals *theGlobals,AEDescList * ioCommands)
  957. // -----------------------------------------------------------     
  958. // Checks to see if the application is in our list
  959. // Adds it to the menu, only adds a Spec if it wasn't there before, but always adds the menu item.
  960. //
  961. OSStatus HaveThisAPPL(FSSpec theSpec,IterateGlobals *theGlobals,AEDescList * ioCommands)
  962. {
  963.     OSErr theErr = noErr;
  964.     UInt16 i;
  965.     HLock((Handle)theGlobals->applicationsHdl);
  966.     for(i = 0; i <  theGlobals->numItems; i++){
  967.         if ((*theGlobals->applicationsHdl)[i].vRefNum == theSpec.vRefNum){
  968.             if ((*theGlobals->applicationsHdl)[i].parID == theSpec.parID) {
  969.                 if ((*theGlobals->applicationsHdl)[i].name[0] == theSpec.name[0]){
  970.                     // If it isn't exactly equal then it is different
  971.                     if (EqualString(theSpec.name, (*theGlobals->applicationsHdl)[i].name, false, false))
  972.                         break;
  973.                 }
  974.             }
  975.         }
  976.     }
  977.     HUnlock((Handle)theGlobals->applicationsHdl);
  978.     if (GetHandleSize((Handle)theGlobals->applicationsHdl) <= (theGlobals->numItems * sizeof(FSSpec))){
  979.         SetHandleSize((Handle)theGlobals->applicationsHdl,(theGlobals->numItems + kHandleInc)*sizeof(FSSpec));
  980.         theErr = MemError();
  981.         }
  982.     if (theErr == noErr){    
  983.         HLock((Handle)theGlobals->applicationsHdl);
  984.         if ( i == theGlobals->numItems){
  985.             ::BlockMoveData(&theSpec,&((*theGlobals->applicationsHdl)[i]),sizeof(FSSpec));
  986.             theGlobals->numItems++;
  987.         }
  988.         HUnlock((Handle)theGlobals->applicationsHdl);
  989.         theErr = AddCommandToAEDescList(    theGlobals->itemName,i,ioCommands); // the Alias files name
  990.     }    
  991.     return theErr;    
  992. }        
  993.  
  994.     /*-----------------------------*
  995.      | ResolveAliasWithMountFlags |
  996.      *-----------------------------*/
  997.  
  998.  
  999.     pascal OSErr ResolveAliasFileWithMountFlags    (FSSpec *                fileFSSpec,
  1000.                                  Boolean                 resolveAliasChains,
  1001.                                  Boolean *                targetIsFolder,
  1002.                                  Boolean *                wasAliased,
  1003.                                  unsigned long             mountFlags) 
  1004.     {
  1005.  
  1006.     /* maximum number of aliases to resolve before giving up */
  1007.     #define MAXCHAINS 10
  1008.  
  1009.       short myResRefNum;
  1010.       Handle alisHandle;
  1011.       FSSpec initFSSpec;
  1012.       Boolean updateFlag, foundFlag, wasAliasedTemp, specChangedFlag;
  1013.       short chainCount;
  1014.       OSErr retCode;
  1015.       Boolean mountRemoteVols = (mountFlags != kResolveAliasFileNoUI );
  1016.  
  1017.       if (fileFSSpec == nil || targetIsFolder == nil || wasAliased == nil)
  1018.         return paramErr;
  1019.  
  1020.       initFSSpec = *fileFSSpec; /* so FSSpec can be restored in case of error */
  1021.       chainCount = MAXCHAINS;   /* circular alias chain protection */
  1022.       myResRefNum = -1;         /* resource file not open */
  1023.  
  1024.       *targetIsFolder = foundFlag = specChangedFlag = false;
  1025.  
  1026.       /* loop through chain of alias files */
  1027.       do {
  1028.         chainCount--;
  1029.  
  1030.         /* check if FSSpec is an alias file or a directory */
  1031.         /* note that targetIsFolder => NOT wasAliased      */
  1032.  
  1033.         retCode = IsAliasFile(fileFSSpec, wasAliased, targetIsFolder);
  1034.         if (retCode != noErr || !(*wasAliased)) break;
  1035.  
  1036.         /* get the resource file reference number */
  1037.         myResRefNum = FSpOpenResFile(fileFSSpec, fsCurPerm);
  1038.         retCode = ResError();
  1039.         if (myResRefNum == -1) break;
  1040.  
  1041.         /* the first 'alis' resource in the file is the appropriate alias */
  1042.         alisHandle = Get1IndResource(rAliasType, 1);
  1043.         retCode = ResError();
  1044.         if (alisHandle == nil) break;
  1045.  
  1046.         /* load the resource explicitly in case SetResLoad(FALSE) */
  1047.         LoadResource(alisHandle);
  1048.         retCode = ResError();
  1049.         if (retCode != noErr) break;
  1050.  
  1051.         retCode = FollowFinderAlias(fileFSSpec, (AliasHandle) alisHandle,
  1052.           mountRemoteVols, fileFSSpec, &updateFlag);
  1053.         /* FollowFinderAlias returns nsvErr if volume not mounted */
  1054.  
  1055.         if (retCode == noErr) {
  1056.  
  1057.           if (updateFlag) {
  1058.             /* the resource in the alias file needs updating */
  1059.             ChangedResource(alisHandle);
  1060.             WriteResource(alisHandle);
  1061.           }
  1062.  
  1063.           specChangedFlag = true; /* in case of error, restore file spec */
  1064.  
  1065.           retCode = IsAliasFile(fileFSSpec, &wasAliasedTemp, targetIsFolder);
  1066.           if (retCode == noErr)
  1067.             /* we're done unless it was an alias file and we're following a chain */
  1068.             foundFlag = !(wasAliasedTemp && resolveAliasChains);
  1069.  
  1070.         }
  1071.  
  1072.         CloseResFile(myResRefNum);
  1073.         myResRefNum = -1;
  1074.  
  1075.       } while (retCode == noErr && chainCount > 0 && !foundFlag);
  1076.  
  1077.       /* return file not found error for circular alias chains */
  1078.       if (chainCount == 0 && !foundFlag) retCode = fnfErr;
  1079.  
  1080.       /* if error occurred, close resource file and restore the original FSSpec */
  1081.  
  1082.       if (myResRefNum != -1) CloseResFile(myResRefNum);
  1083.       if (retCode != noErr && specChangedFlag) *fileFSSpec = initFSSpec;
  1084.  
  1085.       return retCode;
  1086.     }
  1087.  
  1088.     /*-------------*
  1089.      | IsAliasFile |
  1090.      *-------------*/
  1091.  
  1092.     pascal OSErr IsAliasFile(const FSSpec *fileFSSpec,
  1093.                              Boolean *aliasFileFlag,
  1094.                              Boolean *folderFlag)
  1095.     /* sets aliasFileFlag if the FSSpec points to an alias file;
  1096.        sets folderFlag if the FSSpec points to a folder */
  1097.  
  1098.     {
  1099.       CInfoPBRec myCInfoPBRec;
  1100.       OSErr retCode;
  1101.  
  1102.       if (fileFSSpec == nil || aliasFileFlag == nil || folderFlag == nil)
  1103.         return paramErr;
  1104.  
  1105.       *aliasFileFlag = *folderFlag = false;
  1106.  
  1107.       /* get the item's catalog information */
  1108.       myCInfoPBRec.hFileInfo.ioCompletion = nil;
  1109.       myCInfoPBRec.hFileInfo.ioNamePtr = (StringPtr)&fileFSSpec->name;
  1110.       myCInfoPBRec.hFileInfo.ioVRefNum = fileFSSpec->vRefNum;
  1111.       myCInfoPBRec.hFileInfo.ioDirID = fileFSSpec->parID;
  1112.       myCInfoPBRec.hFileInfo.ioFVersNum = 0;  /* MFS compatibility, see TN #204 */
  1113.       myCInfoPBRec.hFileInfo.ioFDirIndex = 0;
  1114.  
  1115.       retCode = PBGetCatInfoSync(&myCInfoPBRec);
  1116.  
  1117.       /* set aliasFileFlag if the item is not a directory and the
  1118.          aliasFile bit is set */
  1119.  
  1120.       if (retCode == noErr) {
  1121.         /* check directory bit */
  1122.         if ((myCInfoPBRec.hFileInfo.ioFlAttrib & ioDirMask) != 0)
  1123.           *folderFlag = true;
  1124.  
  1125.         /* check isAlias bit */
  1126.         else if ((myCInfoPBRec.hFileInfo.ioFlFndrInfo.fdFlags & 0x8000) != 0)
  1127.           *aliasFileFlag = true;
  1128.       }
  1129.  
  1130.       return retCode;
  1131.     }    
  1132.     
  1133. pascal OSErr ResolveAliasWithMountFlags        (const FSSpec *            fromFile,
  1134.                                  AliasHandle             alias,
  1135.                                  FSSpec *                target,
  1136.                                  Boolean *                wasChanged,
  1137.                                  unsigned long             mountFlags)
  1138. {   
  1139.     OSErr theErr= noErr;
  1140.     Boolean logon = (mountFlags != kResolveAliasFileNoUI);
  1141.     Boolean wasAliasedTemp,targetIsFolder;
  1142.     *wasChanged = false;
  1143.     if ((alias == nil) || (target == nil) || (wasChanged == nil)) return paramErr;
  1144.     FSSpec initSpec = *fromFile;
  1145.     theErr =  FollowFinderAlias                (fromFile,
  1146.                                              alias,
  1147.                                              logon,
  1148.                                              target,
  1149.                                              wasChanged);
  1150.     if (theErr != noErr) {
  1151.         return theErr;
  1152.     }
  1153.     
  1154.     theErr = IsAliasFile(target, &wasAliasedTemp, &targetIsFolder);    
  1155.         if (theErr != noErr) {
  1156.         return theErr;
  1157.     }
  1158.     if (wasAliasedTemp){
  1159.         theErr =     ResolveAliasFileWithMountFlags    (target,
  1160.                                                      true,
  1161.                                                      &targetIsFolder,
  1162.                                                      &wasAliasedTemp,
  1163.                                                       mountFlags) ;
  1164.     }
  1165.     return theErr;
  1166. }                                                                                               
  1167.